package com.uam.web.common.utils;


import com.uam.common.constant.CommonConstant;

import java.awt.geom.Point2D;
import java.text.DecimalFormat;
import java.util.List;

/**
 * createTime：2020/11/27 15:05
 *
 * @author zhouyn
 * @version 1.0
 * @since 1.0
 */
public class GeoUtil {
    /**
     * 地球半径
     */
    private static double EARTH_RADIUS = 6378138.0;

    private static double rad(double d) {
        return d * Math.PI / 180.0;
    }
    /** 180° **/
    private static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("0.000000");

    /**
     * 2     * 计算是否在圆上（单位/千米）
     * 3     *
     * 4     * @Title: GetDistance
     * 5     * @Description: TODO()
     * 6     * @param radius 半径
     * 7     * @param lat1  纬度
     * 8     * @param lng1  经度
     * 9     * @return
     * 10     * @return double
     * 11     * @throws
     * 12
     */
    public static boolean isInCircle(double radius, double lat1, double lng1, double lat2, double lng2) {
        double radLat1 = rad(lat1);
        double radLat2 = rad(lat2);
        double a = radLat1 - radLat2;
        double b = rad(lng1) - rad(lng2);
        double s = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2) + Math.cos(radLat1) * Math.cos(radLat2) * Math.pow(Math.sin(b / 2), 2)));
        s = s * EARTH_RADIUS;
        s = Math.round(s * 10000) / 10000;
        if (s > radius) {
            return false;
        } else {
            return true;
        }
    }

    /**
     * 根据一点的坐标与距离，以及方向，计算另外一点的位置
     * @param angle 角度，从正北顺时针方向开始计算
     * @param startLong 起始点经度
     * @param startLat 起始点纬度
     * @param distance 距离，单位m
     * @return
     */
    public static double[] distanceAndGpsToPoint(double angle, double startLong, double startLat, double distance){
        double[] result = new double[2];
        //将距离转换成经度的计算公式
        double tempDistance = distance/EARTH_RADIUS;
        // 转换为radian，否则结果会不正确
        angle = Math.toRadians(angle);
        startLong = Math.toRadians(startLong);
        startLat = Math.toRadians(startLat);
        double lat = Math.asin(Math.sin(startLat)*Math.cos(tempDistance)+Math.cos(startLat)*Math.sin(tempDistance)*Math.cos(angle));
        double lon = startLong + Math.atan2(Math.sin(angle)*Math.sin(tempDistance)*Math.cos(startLat),Math.cos(tempDistance)-Math.sin(startLat)*Math.sin(lat));
        // 转为正常的10进制经纬度
        lon = Math.toDegrees(lon);
        lat = Math.toDegrees(lat);
        result[0] = Double.valueOf(DECIMAL_FORMAT.format(lon));
        result[1] = Double.valueOf(DECIMAL_FORMAT.format(lat));
        return result;
    }

    /**
     * 是否在矩形区域内
     *
     * @param lat    测试点经度
     * @param lng    测试点纬度
     * @param minLat 纬度范围限制1
     * @param maxLat 纬度范围限制2
     * @param minLng 经度限制范围1
     * @param maxLng 经度范围限制2
     * @return boolean
     * @throws
     * @Title: isInArea
     * @Description: TODO()
     */
    public static boolean isInRectangleArea(double lat, double lng, double minLat,
                                            double maxLat, double minLng, double maxLng) {
        if (isInRange(lat, minLat, maxLat)) {
            if (minLng * maxLng > 0) {
                if (isInRange(lng, minLng, maxLng)) {
                    return true;
                } else {
                    return false;
                }
            } else {
                if (Math.abs(minLng) + Math.abs(maxLng) < CommonConstant.ONE_HUNDRED_AND_EIGHTY) {
                    if (isInRange(lng, minLng, maxLng)) {
                        return true;
                    } else {
                        return false;
                    }
                } else {
                    double left = Math.max(minLng, maxLng);
                    double right = Math.min(minLng, maxLng);
                    if (isInRange(lng, left, CommonConstant.ONE_HUNDRED_AND_EIGHTY) || isInRange(lng, right, -CommonConstant.ONE_HUNDRED_AND_EIGHTY)) {
                        return true;
                    } else {
                        return false;
                    }
                }
            }
        } else {
            return false;
        }
    }

    /**
     * 判断是否在经纬度范围内
     *
     * @param point
     * @param left
     * @param right
     * @return boolean
     * @throws
     * @Title: isInRange
     * @Description: TODO()
     */
    public static boolean isInRange(double point, double left, double right) {
        if (point >= Math.min(left, right) && point <= Math.max(left, right)) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * 判断点是否在多边形内
     *
     * @param point 测试点
     * @param pts   多边形的点
     * @return boolean
     * @throws
     * @Title: IsPointInPoly
     * @Description: TODO()
     */
    public static boolean isInPolygon(Point2D.Double point, List<Point2D.Double> pts) {

        int size = pts.size();
        boolean boundOrVertex = true;
        int intersectCount = 0;
        double precision = 2e-10;
        Point2D.Double p1, p2;
        Point2D.Double p = point;
        p1 = pts.get(0);
        for (int i = 1; i <= size; ++i) {
            if (p.equals(p1)) {
                return boundOrVertex;
            }

            p2 = pts.get(i % size);
            if (p.x < Math.min(p1.x, p2.x) || p.x > Math.max(p1.x, p2.x)) {
                p1 = p2;
                continue;
            }

            //射线穿过算法
            if (p.x > Math.min(p1.x, p2.x) && p.x < Math.max(p1.x, p2.x)) {
                if (p.y <= Math.max(p1.y, p2.y)) {
                    if (p1.x == p2.x && p.y >= Math.min(p1.y, p2.y)) {
                        return boundOrVertex;
                    }

                    if (p1.y == p2.y) {
                        if (p1.y == p.y) {
                            return boundOrVertex;
                        } else {
                            ++intersectCount;
                        }
                    } else {
                        double xinters = (p.x - p1.x) * (p2.y - p1.y) / (p2.x - p1.x) + p1.y;
                        if (Math.abs(p.y - xinters) < precision) {
                            return boundOrVertex;
                        }

                        if (p.y < xinters) {
                            ++intersectCount;
                        }
                    }
                }
            } else {
                if (p.x == p2.x && p.y <= p2.y) {
                    Point2D.Double p3 = pts.get((i + 1) % size);
                    if (p.x >= Math.min(p1.x, p3.x) && p.x <= Math.max(p1.x, p3.x)) {
                        ++intersectCount;
                    } else {
                        intersectCount += 2;
                    }
                }
            }
            p1 = p2;
        }
        if (intersectCount % CommonConstant.TWO == 0) {
            return false;
        } else { //奇数在多边形内
            return true;
        }
    }


    public static boolean isPoint(String param) {
        try {
            String[] paramArray = param.split("\\,");
            Double longitude = new Double(paramArray[0]);
            Double latitude = new Double(paramArray[1]);
            if (longitude > CommonConstant.ONE_HUNDRED_AND_EIGHTY || longitude < -CommonConstant.ONE_HUNDRED_AND_EIGHTY) {
                return false;
            } else if (latitude > CommonConstant.NINETY_ZERO || latitude < -CommonConstant.NINETY_ZERO) {
                return false;
            }
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    public static boolean isLine(String param) {
        try {
            param = param.substring(1, param.length() - 1);
            String[] paramArray = param.split("],\\[");
            for (String currentParam : paramArray) {
                if (!isPoint(currentParam)) {
                    return false;
                }
            }
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    public static boolean isArea(String param) {
        if (!isLine(param)) {
            return false;
        }
        param = param.substring(1, param.length() - 1);
        String[] paramArray = param.split("],\\[");
        return paramArray[0].equals(paramArray[paramArray.length - 1]);
    }


}
